﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Db8.Data;
using Db8.DataAccess;

namespace Db8.Controllers
{
    public class AdminController : ControllerBase
    {
        private Db8Context db = new Db8Context();

        [Authorize(Roles = "System Administrator")]
        public ViewResult Topics()
        {
            return View(db.Keywords.OrderByDescending(k => k.ClaimCount).ToList());
        }

        [Authorize(Roles = "System Administrator")]
        public ActionResult CleanTopics()
        {

            // group count, then update
            var keywords = db.Keywords.ToList();
            var groups = db.ClaimKeywords.GroupBy(ck => ck.KeywordId).Select(g => new { KeywordId = g.Key, Count = g.Count() }).ToList();

            var mismatches = groups.Where(g => g.Count != keywords.Where(kw => kw.KeywordId == g.KeywordId).Select(kw => kw.ClaimCount).FirstOrDefault());
            foreach (var g in mismatches)
            {
                var keyword = keywords.Where(kw => kw.KeywordId == g.KeywordId).FirstOrDefault();
                if (keyword != null)
                    keyword.ClaimCount = g.Count;
            }


            // delete keywords with zero uses
            var notFound = keywords.Where(kw => !groups.Any(g => g.KeywordId == kw.KeywordId)).ToArray();
            var zeroCountKeywords = db.Keywords.Where(kw => kw.ClaimCount == 0).ToArray();
            for (int i = 0; i < notFound.Length; i++)
            {
                db.Keywords.Remove(notFound[i]);
            }
            for (int i = 0; i < zeroCountKeywords.Length; i++)
            {
                db.Keywords.Remove(zeroCountKeywords[i]);
            }


            // save
            db.SaveChanges();

            return RedirectToAction("Topics");
        }

        [HttpPost, Authorize(Roles = "System Administrator")]
        public ActionResult EditTopic(int keyword, string replaceWith)
        {
            var keywordToUpdate = db.Keywords.Where(kw => kw.KeywordId == keyword).FirstOrDefault();
            var claims = db.ClaimKeywords.Where(ck => ck.KeywordId == keyword).Select(ck => ck.ClaimId).ToArray();

            if (keywordToUpdate != null)
            {
                string[] newKeywords = replaceWith.Split(',').Select(kw => kw.Trim()).Where(kw => kw != "").ToArray();
                string sameWord = newKeywords.Where(nk => nk == keywordToUpdate.Value).FirstOrDefault();
                List<string> differentWords = newKeywords.Where(nk => nk != keywordToUpdate.Value).ToList();
                while (differentWords.Count > 0)
                {
                    string word = differentWords[0];
                    var mergeWith = db.Keywords.Where(kw => kw.KeywordId != keyword && kw.Value == word).FirstOrDefault();
                    if (mergeWith == null)
                    {
                        if (sameWord == null)
                        {
                            keywordToUpdate.Value = word;
                            sameWord = word;
                        }
                        else
                        {
                            var newKeyword = new Keyword() { Value = word, ClaimCount = claims.Length };
                            db.Keywords.Add(newKeyword);
                            foreach (var claim in claims)
                            {
                                db.ClaimKeywords.Add(new ClaimKeyword { ClaimId = claim, Keyword = newKeyword });
                            }
                        }
                    }
                    else
                    {
                        var claimsAlreadyThere = db.ClaimKeywords.Where(ck => ck.KeywordId == mergeWith.KeywordId).Select(ck => ck.ClaimId).ToArray();
                        var claimsToMerge = claims.Except(claimsAlreadyThere);
                        foreach (var claimToMerge in claimsToMerge)
                        {
                            db.ClaimKeywords.Add(new ClaimKeyword(){ ClaimId = claimToMerge, KeywordId = mergeWith.KeywordId });
                        }
                        mergeWith.ClaimCount += claimsToMerge.Count();
                    }
                    differentWords.RemoveAt(0);
                }

                // delete merged-out keyword
                if (sameWord == null)
                {
                    var cascades = db.ClaimKeywords.WhereIn(ck => ck.ClaimId, claims).Where(ck => ck.KeywordId == keywordToUpdate.KeywordId).ToArray();
                    foreach (var ck in cascades)
                    {
                        db.ClaimKeywords.Remove(ck);
                    }
                    db.Keywords.Remove(keywordToUpdate);
                }

                db.SaveChanges();
            }

            return RedirectToAction("Topics");
        }

        [Authorize(Roles = "System Administrator")]
        public ViewResult Users(string filter)
        {
            if (filter == null)
                filter = "";
            var users = db.Users.Include("UserRoles").Include("UserRoles.Role")
                .Where(u => u.Name.Contains(filter)).ToList();
            return View(users);
        }

        [HttpPost, Authorize(Roles = "System Administrator")]
        public ActionResult SetIsInRole(long user, string role, bool isInRole)
        {
            int? roleId = db.Roles.Where(r => r.Name == role).Select(r => (int?)r.RoleId).FirstOrDefault();
            if (roleId == null)
                throw new ApplicationException("Role '" + role + "' not found");

            var userRole = db.UserRoles.Where(ur => ur.UserId == user && ur.RoleId == roleId.Value).FirstOrDefault();
            if (isInRole && userRole == null)
            {
                db.UserRoles.Add(new UserRole() { UserId = user, RoleId = roleId.Value });
                db.SaveChanges();
                return Json(new { Modified = 1 });
            }
            else if (!isInRole && userRole != null)
            {
                db.UserRoles.Remove(userRole);
                db.SaveChanges();
                return Json(new { Modified = 1 });
            }
            //throw new ApplicationException("No action performed");
            return Json(new { Modified = 0 });
        }



        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}